﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GameStateManagement;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using QS.Input;
using QS.Logic.Modules;
using QS.Logic.Objects;
using QS.ClassLibrary;
using QS.Game.Logic.Modules;
using QS.Game.Renderer;

namespace QS.Editor
{
    class QEditor : GameScreen
    {
        #region Editor Fields

        private int m_Height = 10;
        private int m_Width = 10;
        private int m_LayerCount = 10;

        private int m_ActiveLayer = 0;

        public enum QEditMode
        {
            Default,
            Remove,
            Add
        }

        public enum QAddType
        {
            Cockpit,
            Quarters,
            EngineRoom,
            Turrets,
            Engine,
            Armor,
            ShieldGenerator
        }

        private QEditMode m_EditMode;
        public QEditMode EditMode
        {
            get
            {
                return m_EditMode;
            }
            private set
            {
                m_EditMode = value;
            }
        }

        private QAddType m_AddingType;
        public QAddType AddingType
        {
            get
            {
                return m_AddingType;
            }
            private set
            {
                m_AddingType = value;
            }
        }
        private ContentManager content;

        private GraphicsDevice m_GraphicsDevice;
        private QShipModule[, ,] m_Modules;


        QModularSpaceShip ship;
        QModularSpaceShipModel shipModel;

        private SpriteFont m_EditorFont;
        #endregion

        private QCamera m_ViewerCamera;

        public override void LoadContent()
        {
            if (content == null)
                content = new ContentManager(ScreenManager.Game.Services, "Content");

            m_EditorFont = content.Load<SpriteFont>("Editor");

            ScreenManager.Game.ResetElapsedTime();
            ScreenManager.Game.IsMouseVisible = true;
            m_GraphicsDevice = ScreenManager.GraphicsDevice;
            m_SpriteBatch = new SpriteBatch(m_GraphicsDevice);
            defaultViewport = m_GraphicsDevice.Viewport;
            leftViewport = defaultViewport;
            rightViewport = defaultViewport;
            leftViewport.Width = leftViewport.Width / 2 - 5;
            rightViewport.Width = rightViewport.Width / 2 - 5;
            rightViewport.X = leftViewport.Width + 10;

            m_EditorProjectionMatrix = Matrix.CreateOrthographic(leftViewport.Width, leftViewport.Height, 0.01f, 10000f);
            m_EditorViewMatrix = Matrix.CreateLookAt(new Vector3(0, 0.1f, 0), new Vector3(0, 0, 0), new Vector3(1, 0, 0));
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
    MathHelper.PiOver4, 4.0f / 3.0f, 1.0f, 10000f);
            halfprojectionMatrix = Matrix.CreatePerspectiveFieldOfView(
                MathHelper.PiOver4, 2.0f / 3.0f, 1.0f, 10000f);

            separator = new Texture2D(m_GraphicsDevice, 1, 1);
            separator.SetData<Color>(new Color[1] { Color.White });
            m_Grid = new QEditorGrid();
            m_Grid.Load(m_GraphicsDevice, m_Width, m_Height);
            m_Modules = new QShipModule[m_Width, m_Height, m_LayerCount];

            m_Box = new QBox();
            m_Box.Load(m_GraphicsDevice);

            m_Coords = new QCoordinateSystem();
            m_Coords.Load(m_GraphicsDevice);

            m_ViewerGrid = new QGrid();
            m_ViewerGrid.Load(m_GraphicsDevice, 100, 100);

            m_ViewerCamera = new QFreeCamera(m_GraphicsDevice);
            QMouseOperations.ResetMouse = true;
            ship = new QModularSpaceShip();
            shipModel = new QModularSpaceShipModel(ship);

            QRendererCollection.Initialize(m_GraphicsDevice, content);
        }

        QCoordinateSystem m_Coords;
        QBox m_Box;
        QEditorGrid m_Grid;
        Viewport defaultViewport;
        Viewport leftViewport;
        Viewport rightViewport;
        Matrix projectionMatrix;
        Matrix halfprojectionMatrix;
        SpriteBatch m_SpriteBatch;
        Texture2D separator;
        QGrid m_ViewerGrid;
        Vector3 m_CameraTranslation;

        private Matrix m_EditorProjectionMatrix;
        private Matrix m_EditorViewMatrix;


        public override void UnloadContent()
        {
            content.Unload();
        }

        //Das stimmt noch nicht mit unserem Konzept überein, das muss noch angepasst werden
        public override void HandleInput(InputState input)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            // Look up inputs for the active player profile.
            int playerIndex = (int)ControllingPlayer.Value;

            KeyboardState keyboardState = input.CurrentKeyboardStates[playerIndex];
            GamePadState gamePadState = input.CurrentGamePadStates[playerIndex];

            // The game pauses either if the user presses the pause button, or if
            // they unplug the active gamepad. This requires us to keep track of
            // whether a gamepad was ever plugged in, because we don't want to pause
            // on PC if they are playing with a keyboard and have no gamepad at all!
            bool gamePadDisconnected = !gamePadState.IsConnected &&
                                       input.GamePadWasConnected[playerIndex];

            if (input.IsPauseGame(ControllingPlayer) || gamePadDisconnected)
            {
                ScreenManager.AddScreen(new PauseMenuScreen(), ControllingPlayer);
            }
        }

        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
                                                      bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, false);
            m_GraphicsDevice.Viewport = rightViewport;
            QMouseOperations.ResetMouse = (QMouseOperations.RightDown);
            QMouseOperations.UpdateMouseState(m_GraphicsDevice);

            QKeyboardOperations.UpdateKeyboardState();

            bool isInEditor = leftViewport.Bounds.Contains(QMouseOperations.CurrentPosition);

            ScreenManager.Game.IsMouseVisible = isInEditor;

            if (isInEditor)
                HandleEditorInput();
            else
            {
                m_GraphicsDevice.Viewport = rightViewport;
                m_ViewerCamera.Update(gameTime);
            }

        }

        private void HandleAddMouseDown(Point arrayPoint)
        {
            if (arrayPoint.X != -1 && arrayPoint.Y != -1)
            {
                QShipModule newBox = null;
                switch (m_AddingType)
                {
                    case QAddType.Cockpit:
                        newBox = new QShipCockpit();
                        break;
                    case QAddType.Quarters:
                        newBox = new QShipQuarters();
                        break;
                    case QAddType.EngineRoom:
                        newBox = new QShipEngineRoom();
                        break;
                    case QAddType.Turrets:
                        newBox = new QShipTurret();
                        break;
                    case QAddType.Engine:
                        newBox = new QShipEngine();
                        newBox = (QShipEngine)QObjectFactory.GetInstance(typeof(QShipEngine), m_GraphicsDevice, content, null);
                        break;
                    case QAddType.Armor:
                        newBox = new QShipArmor();
                        break;
                    case QAddType.ShieldGenerator:
                        newBox = new QShipShieldGenerator();
                        break;
                    default:
                        break;
                }
                newBox.ArrayPosition = new IntVector3(arrayPoint.X, arrayPoint.Y, m_ActiveLayer);
                m_Modules[arrayPoint.X, arrayPoint.Y, m_ActiveLayer] = newBox;
                ModulesToShip();
            }
        }

        private void ModulesToShip()
        {
            ship.m_Modules.Clear();
            for (int x = 0; x < m_Width; x++)
                for (int y = 0; y < m_Height; y++)
                    for (int z = 0; z < m_LayerCount; z++)
                    {
                        if (m_Modules[x, y, z] != null)
                            ship.m_Modules.Add(m_Modules[x, y, z]);
                    }
            shipModel.CalcModel();
        }

        private void HandleRemoveMouseDown(Point arrayPoint)
        {
            if (arrayPoint.X != -1 && arrayPoint.Y != -1)
            {
                m_Modules[arrayPoint.X, arrayPoint.Y, m_ActiveLayer] = null;
            }
        }

        private Point ArrayHitTest(Vector2 mouse)
        {
            Point arrayPoint = new Point((int)((mouse.X) / 20f), (int)((mouse.Y) / 20f));
            Console.WriteLine(" Point : " + arrayPoint.ToString());
            if (!(arrayPoint.X < 0 || arrayPoint.X >= m_Width || arrayPoint.Y < 0 || arrayPoint.Y >= m_Height))
            {
                return arrayPoint;
            }
            return new Point(-1, -1);
        }

        private void HandleEditorInput()
        {
            if (QMouseOperations.LeftClicked)
            {
                Vector2 mouse = new Vector2(QMouseOperations.CurrentPosition.X - leftViewport.Width * 0.5f - m_CameraTranslation.Z, QMouseOperations.CurrentPosition.Y - leftViewport.Height * 0.5f + m_CameraTranslation.X);
                Console.WriteLine(" Kamera : " + m_CameraTranslation.ToString());
                Console.WriteLine(" Maus : " + mouse.ToString());
                Point arrayPoint = ArrayHitTest(mouse);
                switch (m_EditMode)
                {
                    case QEditMode.Default:
                        break;
                    case QEditMode.Remove:
                        HandleRemoveMouseDown(arrayPoint);
                        break;
                    case QEditMode.Add:
                        HandleAddMouseDown(arrayPoint);
                        break;
                    default:
                        break;
                }

            }
            if (QMouseOperations.MouseState.RightButton == ButtonState.Pressed)
            {
                m_CameraTranslation.Z += QMouseOperations.PositionDelta.X;
                m_CameraTranslation.X -= QMouseOperations.PositionDelta.Y;
            }

            if (QKeyboardOperations.IsNewKeyPress(Keys.Up))
            {
                m_ActiveLayer++;
                if (m_ActiveLayer >= m_LayerCount)
                    m_ActiveLayer = m_LayerCount - 1;
            }

            if (QKeyboardOperations.IsNewKeyPress(Keys.Down))
            {
                m_ActiveLayer--;
                if (m_ActiveLayer < 0)
                    m_ActiveLayer = 0;
            }
            if (QKeyboardOperations.IsNewKeyPress(Keys.D1))
                EditMode = QEditMode.Default;
            if (QKeyboardOperations.IsNewKeyPress(Keys.D2))
                EditMode = QEditMode.Add;
            if (QKeyboardOperations.IsNewKeyPress(Keys.D3))
                EditMode = QEditMode.Remove;

            if (QKeyboardOperations.IsNewKeyPress(Keys.C))
                AddingType = QAddType.Cockpit;
            if (QKeyboardOperations.IsNewKeyPress(Keys.Q))
                AddingType = QAddType.Quarters;
            if (QKeyboardOperations.IsNewKeyPress(Keys.R))
                AddingType = QAddType.EngineRoom;
            if (QKeyboardOperations.IsNewKeyPress(Keys.E))
                AddingType = QAddType.Engine;
            if (QKeyboardOperations.IsNewKeyPress(Keys.T))
                AddingType = QAddType.Turrets;
            if (QKeyboardOperations.IsNewKeyPress(Keys.S))
                AddingType = QAddType.ShieldGenerator;
            if (QKeyboardOperations.IsNewKeyPress(Keys.A))
                AddingType = QAddType.Armor;

        }

        private void DrawEditor()
        {
            Matrix trans = Matrix.CreateTranslation(m_CameraTranslation);
            m_Coords.Draw(m_GraphicsDevice, m_EditorViewMatrix, m_EditorProjectionMatrix);

            m_Grid.Draw(m_GraphicsDevice, m_EditorViewMatrix, m_EditorProjectionMatrix, Matrix.CreateScale(20f) * trans);

            for (int x = 0; x < m_Width; x++)
            {
                for (int z = 0; z < m_Height; z++)
                {
                    if (m_Modules[x, z, m_ActiveLayer] != null)
                        m_Box.Draw(m_GraphicsDevice, m_EditorViewMatrix, m_EditorProjectionMatrix, Matrix.CreateTranslation(-z - 1, 0f, x) * Matrix.CreateScale(20f) * trans);
                }
            }

            PaintHelp();
        }

        private void PaintHelp()
        {
            Color usedColor = Color.Lime;

            m_SpriteBatch.Begin();

            //m_SpriteBatch.DrawString(m_EditorFont, "Ebene: " + m_ActiveLayer.ToString(), new Vector2(10, 10), usedColor);

            m_SpriteBatch.DrawString(m_EditorFont, "Modes: ", new Vector2(10, 10), usedColor);
            if (m_EditMode == QEditMode.Default)
                usedColor = Color.Orange;
            else
                usedColor = Color.Lime;
            m_SpriteBatch.DrawString(m_EditorFont, " Default: 1 ", new Vector2(10, 25), usedColor);

            if (m_EditMode == QEditMode.Add)
                usedColor = Color.Orange;
            else
                usedColor = Color.Lime;
            m_SpriteBatch.DrawString(m_EditorFont, " Adding: 2 ", new Vector2(10, 40), usedColor);

            if (m_EditMode == QEditMode.Remove)
                usedColor = Color.Orange;
            else
                usedColor = Color.Lime;
            m_SpriteBatch.DrawString(m_EditorFont, " Removing: 3 ", new Vector2(10, 55), usedColor);

            if (m_EditMode == QEditMode.Add)
            {
                int y = 10;
                usedColor = Color.Lime;
                m_SpriteBatch.DrawString(m_EditorFont, "Ship-Parts: ", new Vector2(100, y), usedColor);
                y += 15;

                if (m_AddingType == QAddType.Cockpit)
                    usedColor = Color.Orange;
                else
                    usedColor = Color.Lime;
                m_SpriteBatch.DrawString(m_EditorFont, " Cockpit: C", new Vector2(100, y), usedColor);
                y += 15;
                if (m_AddingType == QAddType.EngineRoom)
                    usedColor = Color.Orange;
                else
                    usedColor = Color.Lime;
                m_SpriteBatch.DrawString(m_EditorFont, " Engineroom: R", new Vector2(100, y), usedColor);
                y += 15;
                if (m_AddingType == QAddType.Quarters)
                    usedColor = Color.Orange;
                else
                    usedColor = Color.Lime;
                m_SpriteBatch.DrawString(m_EditorFont, " Quarters: Q", new Vector2(100, y), usedColor);
                y += 15;
                if (m_AddingType == QAddType.Turrets)
                    usedColor = Color.Orange;
                else
                    usedColor = Color.Lime;
                m_SpriteBatch.DrawString(m_EditorFont, " Turrets: T", new Vector2(100, y), usedColor);
                y += 15;
                if (m_AddingType == QAddType.Engine)
                    usedColor = Color.Orange;
                else
                    usedColor = Color.Lime;
                m_SpriteBatch.DrawString(m_EditorFont, " Engines: E ", new Vector2(100, y), usedColor);
                y += 15;
                if (m_AddingType == QAddType.ShieldGenerator)
                    usedColor = Color.Orange;
                else
                    usedColor = Color.Lime;
                m_SpriteBatch.DrawString(m_EditorFont, " Shieldgenerator: S", new Vector2(100, y), usedColor);
                y += 15;
                if (m_AddingType == QAddType.Armor)
                    usedColor = Color.Orange;
                else
                    usedColor = Color.Lime;
                m_SpriteBatch.DrawString(m_EditorFont, " Armor: A ", new Vector2(100, y), usedColor);
                y += 15;
            }
            m_SpriteBatch.End();
        }

        private void DrawViewer()
        {
            m_ViewerGrid.Draw(ScreenManager.GraphicsDevice, m_ViewerCamera.ViewMatrix, m_ViewerCamera.ProjectionMatrix, Matrix.Identity);
            m_Coords.Draw(ScreenManager.GraphicsDevice, m_ViewerCamera.ViewMatrix, m_ViewerCamera.ProjectionMatrix);

            shipModel.Draw(m_ViewerCamera);
        }

        public override void Draw(GameTime gameTime)
        {
            m_GraphicsDevice.Viewport = defaultViewport;
            m_GraphicsDevice.Clear(Color.CornflowerBlue);

            m_GraphicsDevice.Viewport = leftViewport;
            m_SpriteBatch.Begin();
            m_SpriteBatch.Draw(separator, leftViewport.Bounds, Color.Black);
            m_SpriteBatch.End();

            DrawEditor();

            m_GraphicsDevice.Viewport = rightViewport;
            m_SpriteBatch.Begin();
            m_SpriteBatch.Draw(separator, new Rectangle(0, 0, rightViewport.Width, rightViewport.Height), Color.Black);
            m_SpriteBatch.End();

            DrawViewer();

            m_GraphicsDevice.Viewport = defaultViewport;

            m_SpriteBatch.Begin();
            m_SpriteBatch.Draw(separator, new Rectangle(leftViewport.Bounds.Right, 0, 10, rightViewport.Height), Color.DarkGray);
            m_SpriteBatch.End();

        }
    }



    class QEditorGrid
    {
        private int m_Width = 100;
        private int m_Height = 100;
        private float m_Factor = 10;

        private VertexBuffer m_VertexBuffer;

        private BasicEffect m_Effect;

        public QEditorGrid(float factor = 1)
        {
            m_Factor = factor;
        }

        public void Load(GraphicsDevice gd, int _width, int _height)
        {
            m_Width = _width;
            m_Height = _height;
            m_VertexBuffer = new VertexBuffer(gd, typeof(VertexPositionColor), (m_Width + 1) * 2 + (m_Height + 1) * 2, BufferUsage.WriteOnly);

            VertexPositionColor[] vertices = new VertexPositionColor[(m_Width + 1) * 2 + (m_Height + 1) * 2];

            int i = 0;
            for (int x = 0; x <= m_Height; x++)
            {
                vertices[i++] = new VertexPositionColor(new Vector3(m_Factor * -x, 0f, 0), Color.Lime);
                vertices[i++] = new VertexPositionColor(new Vector3(m_Factor * -x, 0f, m_Factor * m_Width), Color.Lime);
            }
            for (int z = 0; z <= m_Width; z++)
            {
                vertices[i++] = new VertexPositionColor(new Vector3(0, 0f, m_Factor * z), Color.Lime);
                vertices[i++] = new VertexPositionColor(new Vector3(m_Factor * -m_Height, 0f, m_Factor * z), Color.Lime);
            }

            m_VertexBuffer.SetData<VertexPositionColor>(vertices);

            m_Effect = new BasicEffect(gd);
        }

        public void Draw(GraphicsDevice gd, Matrix view, Matrix proj, Matrix world)
        {
            gd.SetVertexBuffer(m_VertexBuffer);

            m_Effect.World = world;
            m_Effect.View = view;
            m_Effect.Projection = proj;
            m_Effect.VertexColorEnabled = true;

            foreach (EffectPass pass in m_Effect.CurrentTechnique.Passes)
            {
                pass.Apply();
                gd.DrawPrimitives(PrimitiveType.LineList, 0, m_VertexBuffer.VertexCount);
            }
        }
    }

    class QBox
    {
        private VertexBuffer m_VertexBuffer;
        private IndexBuffer m_IndexBuffer;
        private BasicEffect m_Effect;

        public QBox()
        {
        }

        public void Load(GraphicsDevice gd)
        {
            m_VertexBuffer = new VertexBuffer(gd, typeof(VertexPositionColor), 4, BufferUsage.WriteOnly);

            VertexPositionColor[] vertices = new VertexPositionColor[4];
            vertices[0] = new VertexPositionColor(new Vector3(-0, 0f, -0), Color.Red);
            vertices[1] = new VertexPositionColor(new Vector3(1, 0f, -0), Color.Red);
            vertices[2] = new VertexPositionColor(new Vector3(1, 0f, 1), Color.Red);
            vertices[3] = new VertexPositionColor(new Vector3(-0, 0f, 1), Color.Red);

            m_VertexBuffer.SetData<VertexPositionColor>(vertices);
            short[] indices = new short[6];
            indices[0] = 2; indices[1] = 1; indices[2] = 0;
            indices[3] = 2; indices[4] = 0; indices[5] = 3;


            m_IndexBuffer = new IndexBuffer(gd, typeof(short), 6, BufferUsage.WriteOnly);
            m_IndexBuffer.SetData(indices);
            m_Effect = new BasicEffect(gd);
        }

        public void Draw(GraphicsDevice gd, Matrix view, Matrix proj, Matrix world)
        {
            gd.SetVertexBuffer(m_VertexBuffer);
            gd.Indices = m_IndexBuffer;
            m_Effect.World = world;
            m_Effect.View = view;
            m_Effect.Projection = proj;
            m_Effect.VertexColorEnabled = true;

            RasterizerState rasterizerState = new RasterizerState();
            rasterizerState.CullMode = CullMode.None;
            gd.RasterizerState = rasterizerState;

            foreach (EffectPass pass in m_Effect.CurrentTechnique.Passes)
            {
                pass.Apply();
                gd.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, 2);
            }
        }
    }
}
